package run

import (
	"fmt"
	"go-redis/utils"
)

type DictEntry struct {
	Key  string
	Val  interface{}
	Next *DictEntry
}

const (
	READ      = "READ"
	WRITE     = "WRITE"
	TABLE_CAP = 1000
)

type DictType struct {
}
type DictHt struct {
	Table    []*DictEntry
	Size     int
	SizeMask int
	Used     int
}

type Dict struct {
	HashTable [2]DictHt
	RehashIdx int
	Type      DictType
}

func NewDict() *Dict {
	return &Dict{
		HashTable: [2]DictHt{
			{
				Table:    make([]*DictEntry, TABLE_CAP),
				Size:     TABLE_CAP,
				SizeMask: TABLE_CAP - 1,
				Used:     0,
			},
		},
		RehashIdx: -1,
		Type:      DictType{},
	}
}

func (t DictType) HashFunction(key string) int {
	return int(utils.Murmurhash2([]byte(key), 0))
}
func (d *Dict) ShouldRehash() bool {
	if ratio := float32(d.HashTable[0].Used / d.HashTable[0].Size); ratio > 1 {
		d.RehashIdx += 1
		utils.Log.Info(fmt.Sprintf("rehash trigger. Used %d; Size: %d reshashIdx: %d", d.HashTable[0].Used, d.HashTable[0].Size, d.RehashIdx))
		d.HashTable[1] = DictHt{
			Table:    make([]*DictEntry, d.HashTable[0].Size*2),
			Size:     d.HashTable[0].Size * 2,
			SizeMask: d.HashTable[0].Size*2 - 1,
			Used:     0,
		}
		return true
	}
	return false
}

func (d *Dict) DictIsRehashing() bool {
	return d.RehashIdx != -1
}
func (d *Dict) DoRehashStep() {
	ptr := d.HashTable[0].Table[d.RehashIdx]
	for ptr != nil {
		k := ptr.Key
		loc := d.Type.HashFunction(k) & d.HashTable[1].SizeMask
		ptrNext := ptr.Next
		ptr.Next = d.HashTable[1].Table[loc]
		d.HashTable[1].Table[loc] = ptr
		ptr = ptrNext
	}
	d.HashTable[0].Table[d.RehashIdx] = nil
	d.RehashIdx += 1
	if d.RehashIdx == d.HashTable[0].Size {
		d.RehashIdx = -1
		d.HashTable[0] = d.HashTable[1]
		d.HashTable[1] = DictHt{}
	}
}

func (d *Dict) DictFind(key string) *DictEntry {
	hashVal := d.Type.HashFunction(key)
	if d.DictIsRehashing() || d.ShouldRehash() {
		d.DoRehashStep()
	}
	var res *DictEntry = nil
	for i := 0; i < 2; i++ {
		loc := hashVal & d.HashTable[i].SizeMask
		he := d.HashTable[i].Table[loc]
		for he != nil {
			if he.Key == key {
				res = he
			}
			he = he.Next
		}
		if !d.DictIsRehashing() {
			return res
		}
	}
	return res
}
func (d *Dict) DictAdd(key string, val interface{}) bool {
	if d.DictIsRehashing() || d.ShouldRehash() {
		d.DoRehashStep()
	}
	if exist := d.DictFind(key); exist != nil {
		exist.Val = val
		return true
	}
	hashVal := d.Type.HashFunction(key)
	var table = 0
	if d.DictIsRehashing() {
		table = 1
	}
	loc := d.HashTable[table].Table[hashVal&d.HashTable[table].SizeMask]
	node := DictEntry{
		Key:  key,
		Val:  val,
		Next: loc,
	}
	d.HashTable[table].Table[hashVal&d.HashTable[table].SizeMask] = &node
	d.HashTable[table].Used += 1
	return true
}
func (d *Dict) DictDel(key string) interface{} {
	if d.DictIsRehashing() || d.ShouldRehash() {
		defer d.DoRehashStep()
	}
	hashVal := d.Type.HashFunction(key)
	for i := 0; i <= 1; i++ {
		loc := hashVal & d.HashTable[i].SizeMask
		n := d.HashTable[i].Table[loc]
		pre := d.HashTable[i].Table[loc]
		for n != nil {
			if n.Key == key {
				if pre == n {
					d.HashTable[i].Table[loc] = n.Next
					d.HashTable[i].Used -= 1
					return n.Val
				} else {
					pre.Next = n.Next
				}
				break
			}
			pre = n
			n = n.Next
		}
		if !d.DictIsRehashing() {
			return nil
		}
	}
	return nil
}
